package com.aabte.java8;


import org.springframework.util.ResourceUtils;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Test
public class StreamsTest {

    private enum Status {
        OPEN,
        CLOSE
    }

    private static final class Task {
        private final Status status;

        private final Integer points;

        Task(final Status status, final Integer points) {
            this.status = status;
            this.points = points;
        }

        public Status getStatus() {
            return status;
        }

        public Integer getPoints() {
            return points;
        }

        @Override
        public String toString() {
            return String.format("[%s, %d]", status, points);
        }
    }

    private List<Task> taskList;
    private long totalPoints;


    @BeforeTest
    public void setup() {
        taskList = Arrays.asList(
                new Task(Status.OPEN, 5),
                new Task(Status.OPEN, 13),
                new Task(Status.CLOSE, 8)
        );
    }

    /**
     * 使用strem计算Open的任务的总分数
     * 中间操作返回一个新的stream对象
     * 中间操作总是采用惰性求值方式
     * 串行计算
     */
    @Test
    public void serialComputeTasksTotalPoint() {
        final long totalPointsOfOpenTasks = taskList.stream()
                .filter(task -> Status.OPEN.equals(task.status))
                .mapToLong(Task::getPoints)
                .sum();

        System.out.println("Total points: " + totalPointsOfOpenTasks);
    }

    /**
     * 使用strem计算任务的总分数
     * 中间操作返回一个新的stream对象
     * 中间操作总是采用惰性求值方式
     * 并行计算
     */
    @Test
    public void paiallelComputeTasksTotalPoint() {
        totalPoints = taskList.stream()
                .parallel()
                .map(task -> null == task.getPoints() ? 0 : task.getPoints())
                .reduce(0, Integer::sum);

        System.out.println("Total points(all tasks): " + totalPoints);
    }

    /**
     * groupingBy
     */
    @Test
    public void groupingByTest(){
        Map<Status, List<Task>> groupByResult = taskList.stream()
                .collect(Collectors.groupingBy(Task::getStatus));
        System.out.println(groupByResult);
    }

    @Test(dependsOnMethods = "paiallelComputeTasksTotalPoint")
    public void computeTasksPointsWeight(){
        double totalPointsForDouble = totalPoints * 1.0;
        List<String> result = taskList.stream()
                .mapToInt(Task::getPoints)
                .asLongStream()
                .mapToDouble(points -> points / totalPointsForDouble)
                .boxed()
                .mapToLong(weight -> (long) (weight * 100))
                .mapToObj(percentage -> percentage + "%")
                .collect(Collectors.toList());
        System.out.println(result);
    }

    @Test
    public void fileStreamTest(){
        try {
            Path path = ResourceUtils.getFile("classpath:application.properties").toPath();
            Stream<String> lines = Files.lines(path, StandardCharsets.UTF_8);
            lines.onClose(() -> System.out.println("Done!"))
                    .forEach(System.out::println);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
